home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr48 / coun20.zip / COUN.DOC next >
Text File  |  1993-04-04  |  13KB  |  295 lines

  1.  
  2.  
  3.                   
  4.  
  5.                     Turbo Pascal Record Compress Procedure
  6.  
  7.                                Carl A Franz
  8.                                JFL Consulting
  9.                                "We will sell no software
  10.                                    before it's written"
  11.                                1115 S. Ridgeland
  12.                                Oak Park, Il. 60304
  13.                                (708) 383-1546
  14.                                CServe: 71041,1512
  15.  
  16.  
  17.  
  18.     When UnZiping the COUN.ZIP file you should have received:
  19.                   1) COUN.PAS     - The Compress/Uncompress source.
  20.                   2) TESTCOUN.PAS - Demonstration program for COUN.
  21.                   3) COUN.DOC     - This file.
  22.  
  23.     Quite frankly, this is only useful if you have a database like 
  24. BTrieve or TBTree which allows you to have variable length records in a 
  25. database.
  26.  
  27.     If you can't afford BTrieve (I can't), try TBTree written by a guy 
  28. named Dean Farwell 73240,3335.  I get nothing from Dean to plug his 
  29. product, so my opinion of this product is untainted.  It's great.  You 
  30. can put up a well designed database with Turbo Pascal and the TBTree 
  31. product.  It's much better then the Borland Database toolkit.  I think
  32. it's about $25 now.  A heck of a bang for your buck.
  33.  
  34.  
  35.     Anyway, on to this product.  The routines in COUN compress out
  36. space in your records by removing the extra space in the STRING 
  37. variables.  For instance, if you have a record for an address book like 
  38. the following:
  39.  
  40.                         AddrBook = Record
  41.                             NAME : String[40];
  42.                             ADDR1 : String[40];
  43.                             ADDR2 : String[40];
  44.                             City  : String[25];
  45.                             St    : String[2];
  46.                             Zip   : String[9];
  47.  
  48. You have allocated 162 bytes, however, rarely is all that space used
  49. for actual data.  For instance, my name and address use all of 64 
  50. bytes.  That's a lot of wasted space.  Since I am, in fact, building an 
  51. address book of sorts, and I am planning on keeping several record 
  52. types in one file, I figured I needed to save some space.  Thus I wrote 
  53. these Compress/Uncompress routines for Turbo Pascal Records.
  54.  
  55.  
  56.  
  57.  
  58.    How it works:
  59.  
  60.  
  61.     There are 2 routines.
  62.  
  63. FUNCTION Compress(CMap : STRING; VAR InData; VAR OutData) : INTEGER;
  64.  
  65.     This function accepts a map of your Pascal record (CMap), your
  66. record (InData), and someplace you want the compressed record
  67. information to go (OutData).  I highly suggest that the field you use
  68. for OutData be a byte array as large as the Record you are
  69. compressing.  The function then returns the length of the compressed
  70. record.
  71.  
  72. PROCEDURE UnCompress(CMap : STRING; VAR InData; VAR OutData);
  73.  
  74.     This procedure accepts a map of the record (CMap), your compressed
  75. byte array (Indata), and your record (OutData).  I had been
  76. considering swapping positions of InData and OutData so that the
  77. calling conventions are the same for COMPRESS and UNCOMPRESS but
  78. didn't.  If you want to, go ahead, you've got the source code.
  79.  
  80.  
  81.    CMap, the record map is the most complicated part of this mess.  To
  82. compress and uncompress you record, I need to know what it looks like.
  83. To do this is fairly simple.  I use the word 'fairly' advisedly.
  84.  
  85.     Referring to the Address Book record, the CMap would be
  86. 'S40S40S40S25S2S9'. You should get an idea from that.  Basically, you
  87. tell me, in short hand, what the fields in the record are.  To wit:
  88.  
  89.        I = INTEGER;  2bytes    (Case is irrelevant)
  90.        L = LONGINT;  4
  91.        R = REAL;     6
  92.        B = BYTE;     1
  93.        S = STRING;
  94.        C = CHAR;     1
  95.        P = POINTER;  4
  96.        W = WORD;     2
  97.  
  98.        Types not supported are: enumerated type, single, double, or
  99.                                 comp floating-point types, and set
  100.                                 types.
  101.  
  102.    'S' may have a length behind it to define the declared length of
  103. the  string: ie. STRING[40] is 'S40'.  If there is no length following
  104. the string identifier 'S', I assume the length is 255 bytes, the
  105. length of a string defined STRING.
  106.  
  107.     A number may be used to define a length of data.  If you have 5
  108. byte fields in a row, you can either have them defined as 'BBBBB' or
  109. '5'.  Likewise, if a record contains 2 Integers and a pointer you may
  110. define them as 'IIP' or '8'.  If you have a STRING[40] followed by 5
  111. byte fields, you must separate with a comma (','), i.e. 's40,5'.  Lets
  112. face it 'S405' makes no sense.  Also, an 'S' followed by a number that
  113. is not the strings length must be seporated by a comma.
  114.  
  115.  
  116.  
  117.  
  118.  
  119. IE. if you have a field defined STRING followed by 5 BYTE fields the
  120. 'S5' would be assumed to be a 5 byte string, 'S,5' is a 255 byte
  121. string followed by 5 bytes of whatever.
  122.  
  123.  
  124.  
  125.  
  126.     So, you say you've got arrays.  I can handle that.  Lets say you'd  
  127. defined a record thus:
  128.  
  129.                Rec  = Record
  130.                 StrArray : array [1..25] string[40];
  131.  
  132. No problem.  Arrays can be defined by brackets ('[',']').  A left
  133. bracket '[' followed by the number of items in the array starts an
  134. array definition and an right bracket ']' ends it.  To Wit: '[25s40]'
  135. defines an array of 25 40 byte strings (Array [1..25] STRING[40]).
  136.  
  137.     Arrays can also be nested up to 100 levels deep.  Actually, I've
  138. allowed for 100 levels in my tables but realistically you may have
  139. only 100 symbols of any kind in the CMap string.  If you find a need
  140. to expand the limits, go ahead.  The type definitions L1 and L2 are
  141. where to change them.  These are the Cmap parse tables.
  142.  
  143.  
  144.     There are two fields for flagging errors:
  145.  
  146.      1) COUNERR an integer where:
  147.          1 is a memory allocation error.
  148.          2 is a invalid pnumonic error.
  149.           (I don't recognize a record map token character)
  150.           COUNWHR tells you the character position.
  151.          3 is a bracket mismatch error.
  152.          4 Cmap is too big.
  153.  
  154.      2) COUNWHR an integer field defines the CMap string that
  155.         caused the trouble.
  156.  
  157.     There are a several limits as to what I allow.  Records can only
  158. be 32000 bytes long.  Also, like I said above, the maximum CMAP length 
  159. is 100 characters.  Multi-dimentional arrays are not supported.  Oh, 
  160. you can do it by defining a nested array, but I wouldn't try to define 
  161. a multi-dimentional array which contains strings unless you really under-
  162. stand how Turbo Pascal allocates memory.
  163.  
  164.     A note about the previous paragraph: There are no good reasons for
  165. most of the limitations.  I just didn't need anything bigger.  If,
  166. however, you do deceide to make the Byte Array bigger, there are Turbo
  167. Pascal limitations.  Integers go to +32K so indexes need to be changed
  168. to LongInt or Word.  I'm not sure how big arrays can be, but there is
  169. a limit, look it up.  Also, the obvious limit to the CMAP is 255
  170. characters.  If you come up with any interesting ways around that, let
  171. me know.
  172.  
  173.  
  174.  
  175.  
  176.  
  177.  
  178.  
  179.     When compiled the COUN.PAS unit uses 2264 bytes of code and 53
  180. bytes of data space.  If there is enough interest in this (or if Dean
  181. Farwell askes me to) I will convert this to TASM assembler.  It should
  182. then be faster and smaller.  If someone else wants to do it, that's fine
  183. also.  Please send me the code when your done.
  184.  
  185.  
  186.     The source code is provided for several reasons.  1) I like to see 
  187. what other people are doing, I assume others do too.  2) If someone 
  188. comes up with nifty a way of making these routines faster, smaller, 
  189. more elegant, whatever, I would like to know.
  190.  
  191.     If you use these routines, I don't want money.  Well, yes I do.  If 
  192. you feel like sending me a fiver, go ahead.  What I really want is to 
  193. know if anyone finds them useful.  Drop me a note.  I enjoy chatting 
  194. with others in the field.
  195.  
  196.     If you, God forbid, find any bugs in these routines, please let me
  197. know.  I will fix them and get a new version out to you ASAP.  I'm
  198. very proud of my work, so I really do try hard to provide the best
  199. time will allow.  Also, try fixing them yourself, it's good practice.
  200.  
  201.     I have a 20 month old child in the house so no late night calls.  
  202. Anything after 10pm CST and I'll probably get quite angry.  You're much
  203. more likely to get me via CompuServe then calling by phone.  But if
  204. you must, evenings and weekends are the best time.  I do not, under any
  205. circumstances, accept collect calls.  Deal with it.
  206.  
  207.    Biography: (I saw it in someone elses doc and thought it was a good
  208.                idea)
  209.  
  210.    Carl Franz has been in programming for 13 years.  He has written
  211. code professionally for Univac, Burroughs, DEC, IBM Mainframe, Z80
  212. CP/M, and IBM PC.  Currently I'm a Technical Advisor for a commercial
  213. bank.  I consult on the side when the mood hits me.  The JFL in JFL
  214. Consulting stands for 'Just For Laughs' (not really, but you get the
  215. point).  Need a utility written, give me a buzz, if it sounds like fun
  216. we can work something out.
  217.  
  218. Yet again I'm going to plug TBTree.  The next version will provide
  219. Network support.  It already provides fixed and variable length record
  220. support, record lists, keys of Turbo Pascal variable types, so-so
  221. documentation but good example programs.  Last I looked, it was in
  222. BPROGA Lib 2.  It's a big download (about 300K) but worth it.  All
  223. source code provided.  And, for goodness sake, pay the man his $25, it
  224. isn't alot for what you are getting and he needs to know if anyone is
  225. really using the product.  On top of which, as far as I can tell it's
  226. bugless.
  227.  
  228.  
  229.     Good luck and may the farce be with you.
  230.  
  231.  
  232.  
  233.  
  234.  
  235.                         For Algorythm Freaks
  236.  
  237.  
  238.  
  239.  
  240.     The algorythm for the process is kind of brainless.  (Brainless
  241. means 'Why didn't I think of that earlier').  Basicly, there are 2
  242. tables: 1) L1 absorbes all necessary information about the Tokens
  243. in the CMAP table, 2) L2 allow me to stack Array-Start information to
  244. handle nested arrays.
  245.  
  246.     At its vary basics there are 6 token types: 1) 'S' or string with
  247. an optional length; 2) scaler lengths (numeric values); 3) any of the
  248. rest of the pnumonics which refer to Pascal Types; 4) The start array
  249. left bracket '[' plus iteration value; 5) the end array right bracket
  250. ']'; and 6) the lowly comma.
  251.  
  252.     ParseCMap calls GetToken at the start of each loop.  GetToken
  253. looks at the next value in CMap and loads LP1T with the token type,
  254. whatever the character is, and a length.  The length all Pascal Types
  255. is gotten via a SizeOf, except String (S) for which GetNum is called
  256. to check if there is a numeric character after the 'S'.  If there is a
  257. numeric character, it absorbs characters from CMap until a non-numeric
  258. value is found converting the mess into an integer.  LP1T is later
  259. copied to the next item in the L1 table.
  260.  
  261.     The '[' or Start-Array does something a little different.  For the
  262. most part it works the same as the String 'S' token.  Except, when one
  263. is found an entry is made onto stack L2.  The entry consistes of the
  264. index value of where the '[' entry is in L1.  As '['s are found, each
  265. is pushed onto the stack.  When an Array-End ']' token is found, an
  266. entry in the L2 stack is poped.  This entry contains the index
  267. location of the matching Start-Array.  The Size component of L1 is
  268. then loaded with the location of the matching Start-Array so that when
  269. they are finally processed you will know which entry of the L1 table
  270. to return to for iteration.
  271.  
  272.     I have to apologize about the naming conventions.  I was rereading
  273. some notes on expression parsing and evaluation from college which
  274. used the same stupidly cryptic conventions.  I wasn't feeling
  275. particularly creative at 2:30am so I used them instead of making up
  276. better ones.
  277.  
  278.    On the Compress/DeCompress side you step thru the L1 array and do
  279. what it says.  Except.  When a Start-Array is found the iteration
  280. count is moved from Size to Decr.  Then, upon seeing an End-Array
  281. the Decr of the matching Start-Array is checked:  If 0 then nothing is
  282. done and processing continues to the next item;  else if Decr is not
  283. zero it is decremented and the index address of the matching
  284. Start-Array is loaded to the L1 index.  Remember that the L1 index
  285. will be incrimented before checking the next L1 entry so the
  286. Start-Array will not actually be processed again during the 'array
  287. loop'.  Also, each Start-Array has its own Decr, thus nested array
  288. will process properly.
  289.  
  290.    There is a slightly more effecient way of handling the Array loops,
  291. however it involves another integer in L1 and some somewhat more
  292. complicated code.  Also, I have a blind spot figuring out where I
  293. should be with indexes.  I'm alway one ahead or behind where I should
  294. be.
  295.